﻿
using Microsoft.EntityFrameworkCore;
using WHLRDF.ORM.DbExtensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Loader;
using System.Data;
using System.Data.Common;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using WHLRDF.Log;
using System.Diagnostics;
using System.Reflection;

namespace WHLRDF.ORM.EF
{
    public abstract class AbstractDbContext : DbContext, IAbstractDatabase, IQuery, IDbSource
    {
        #region EF基础
   
        public abstract ProviderType DbProviderType { get; }

        protected string ConnectionString { get; set; }

        public DbProviderFactory DbFactory { get; set; }

        private List<SystemComponent> DbModels { get; set; }

        private DbTransaction dbTransaction = null;
        // 是否已在事务之中
        private bool inTransaction = false;

        #region public bool InTransaction 是否已采用事务
        /// <summary>
        /// 是否已采用事务
        /// </summary>
        public bool InTransaction
        {
            get
            {
                return this.inTransaction;
            }

            set
            {
                this.inTransaction = value;
            }
        }
        #endregion
        /// <summary>
        /// 数据库版本号
        /// </summary>
        public string Version { get; set; }


        public AbstractDbContext()
        {
            ConnectionString = ApplicationEnvironments.Site.ConnectionString;
            DbModels = ApplicationEnvironments.Site.Components.Plug;
            Version = ApplicationEnvironments.Site.Version;
        }

        public AbstractDbContext(string connectionString,string version)
        {
            ConnectionString = connectionString;
            DbModels = ApplicationEnvironments.Site.Components.Plug;
            Version = version;
        }
        public AbstractDbContext(string connectionString, List<SystemComponent> dbModels,string version)
        {
            ConnectionString = connectionString;
            DbModels = dbModels;
            Version = version;
        }

        // public DbSet<User> Users { get; set; }
        public AbstractDbContext(DbContextOptions<AbstractDbContext> options) : base(options)
        {
            
        }

        /// <summary>
        /// 创建映射关系
        /// </summary>
        /// <param name="builder"></param>
        protected override void OnModelCreating(ModelBuilder builder)
        {
            #region 动态加载模型实体
            base.OnModelCreating(builder);
            List<dynamic> dynamics = null;
            if (dynamics == null)
            {
                dynamics = new List<dynamic>();
                if (DbModels != null && DbModels.Count > 0)
                {
                    var basePath = AppDomain.CurrentDomain.BaseDirectory;
                    foreach (var component in DbModels)
                    {
                        if (!component.Opened || component.Model == null || component.Model.Length <= 0)
                        {
                            continue;
                        }
                        foreach (var dll in component.Model)
                        {
                            var typesToRegister = AssemblyLoadContext.Default.LoadFromAssemblyPath(basePath + dll).GetTypes().Where(q => q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null);
                            foreach (var type in typesToRegister)
                            {
                                dynamic configurationInstance = Activator.CreateInstance(type);
                                dynamics.Add(configurationInstance);
                                builder.ApplyConfiguration(configurationInstance);
                            }
                        }

                    }
                }

                return;
            }
            if (dynamics == null || dynamics.Count <= 0)
            {
                return;
            }
            foreach (var type in dynamics)
            {
                builder.ApplyConfiguration(type);
            }
            #endregion
        }


        /// <summary>
        /// 初始化数据库
        /// </summary>
        /// <returns></returns>
        public bool DbEnsureCreated()
        {
            if (this.Database != null && this.Database.EnsureCreated())
            {
                return true;
                //object instance = ReflectionHelper.Create(ApplicationEnvironments.Site.InitData);
                //if (instance != null)
                //{
                //    var method = instance.GetType().GetMethod("InsertData", new Type[] { typeof(IDbRepository) });
                //    method.Invoke(instance, new object[] { this });
                //}
                ////_dbContext.Database.GenerateCreateScript();
            }
            return false;

        }
        #endregion


        #region EF中无效方法
        /// <summary>
        /// 这时主要的获取数据库连接的方法
        /// </summary>
        /// <returns>数据库连接</returns>
        public IDbConnection Open()
        {
#if (DEBUG)
            int milliStart = Environment.TickCount;
#endif
            // 这里是获取一个连接的详细方法
            if (String.IsNullOrEmpty(this.ConnectionString))
            {
                this.ConnectionString = ApplicationEnvironments.Site.ConnectionString;
            }
            this.Open(this.ConnectionString);
            // 写入调试信息
#if (DEBUG)
            Trace.WriteLine(DateTime.Now.ToString("HH:mm:ss") + MethodBase.GetCurrentMethod().ReflectedType.Name + "." + MethodBase.GetCurrentMethod().Name);
#endif
            return this.dbConnection;
        }

        public IDbConnection Open(string connectionString)
        {
            // 写入调试信息
#if (DEBUG)
            int milliStart = Environment.TickCount;
            Trace.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " :Begin: " + MethodBase.GetCurrentMethod().ReflectedType.Name + "." + MethodBase.GetCurrentMethod().Name);
#endif
            // 这里数据库连接打开的时候，就判断注册属性的有效性
            //if (!Provider.CheckRegister())
            //{
            //    // 若没有进行注册，让程序无法打开数据库比较好。
            //    connectionString = string.Empty;

            //    // 抛出异常信息显示给客户
            //    throw new Exception("connectionString is Empty");
            //}
            // 若是空的话才打开
            if (this.dbConnection == null || this.dbConnection.State == ConnectionState.Closed)
            {
                this.ConnectionString = connectionString;
                this.dbConnection = this.Database.GetDbConnection();
                
                //this.dbConnection.ConnectionString = this.ConnectionString;
                if (this.dbConnection.State == ConnectionState.Closed)
                {
                    this.dbConnection.Open();
                }
           

                // 创建对象
                // this.dbCommand = this.DbConnection.CreateCommand();
                // this.dbCommand.Connection = this.dbConnection;
                // this.dbDataAdapter = this.dbProviderFactory.CreateDataAdapter();
                // this.dbDataAdapter.SelectCommand = this.dbCommand;

                // 写入调试信息
#if (DEBUG)
                int milliEnd = Environment.TickCount;
                Trace.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " Ticks: " + TimeSpan.FromMilliseconds(milliEnd - milliStart).ToString() + " :End: " + MethodBase.GetCurrentMethod().ReflectedType.Name + "." + MethodBase.GetCurrentMethod().Name);
#endif
            }
            return this.dbConnection;
        }

        public IDbConnection GetDbConnection()
        {
            return this.Database.GetDbConnection();
        }

        public IDbTransaction GetDbTransaction()
        {
            return null;
        }
        #endregion



        #region public DbConnection DbConnection 数据库适配器
        private DbConnection dbConnection = null;
        private bool autoOpenClose = false;
        /// <summary>
        /// 默认打开关闭数据库选项（默认为否）
        /// </summary>
        public bool AutoOpenClose
        {
            get
            {
                return autoOpenClose;
            }
            set
            {
                autoOpenClose = value;
            }
        }
        /// <summary>
        /// 数据库连接
        /// </summary>
        public DbConnection DbConnection
        {
            get
            {
                if (this.dbConnection == null || this.dbConnection.State != ConnectionState.Open)
                {
                    // 若没打开，就变成自动打开关闭的
                    this.Open();
                    this.AutoOpenClose = true;
                }
                return this.dbConnection;
            }
            set
            {
                this.dbConnection = value;
            }
        }
        #endregion
        #region Ado数据操作

        public IDataReader ExecuteReader(string commandText)
        {
            return this.ExecuteReader(commandText,null);
        }
        
        public IDataReader ExecuteReader(string commandText, DbParameter[] dbParameters)
        {
            return ExecuteReader(CommandType.Text,commandText,dbParameters);
        }

        public IDataReader ExecuteReader(CommandType commandType, string commandText, DbParameter[] dbParameters)
        {
            int milliStart = Environment.TickCount;
            var concurrencyDetector = this.Database.GetService<IConcurrencyDetector>();
            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = this.Database.GetService<IRawSqlCommandBuilder>().Build(commandText, dbParameters);

                RelationalDataReader query = rawSqlCommand.RelationalCommand.ExecuteReader(new RelationalCommandParameterObject(this.Database.GetService<IRelationalConnection>(),
                     parameterValues: rawSqlCommand.ParameterValues,null, this, null));
                int milliEnd = Environment.TickCount;
                this.WriteLog(commandText, TimeSpan.FromMilliseconds(milliEnd - milliStart).TotalMilliseconds);
                return query.DbDataReader;
            }
        }
       
        public int ExecuteNonQuery(string commandText)
        {
            return ExecuteNonQuery(commandText,null);
        }
        
        public int ExecuteNonQuery(string commandText, DbParameter[] dbParameters)
        {
            int milliStart = Environment.TickCount;
            var concurrencyDetector = this.Database.GetService<IConcurrencyDetector>();

            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = this.Database.GetService<IRawSqlCommandBuilder>().Build(commandText, dbParameters);

                int value = rawSqlCommand.RelationalCommand.ExecuteNonQuery(new RelationalCommandParameterObject(this.Database.GetService<IRelationalConnection>(),
                     parameterValues: rawSqlCommand.ParameterValues, null, this, null));
                int milliEnd = Environment.TickCount;
                this.WriteLog(commandText, TimeSpan.FromMilliseconds(milliEnd - milliStart).TotalMilliseconds);
                return value;
            }
        }
        
        public int ExecuteNonQuery(CommandType commandType, string commandText, DbParameter[] dbParameters)
        {
            int milliStart = Environment.TickCount;
            int result = 0;
            // 自动打开
            if (this.AutoOpenClose)
            {
                this.Open();
            }
            else
            {
                if (this.DbConnection == null)
                {
                    this.AutoOpenClose = true;
                    this.Open();
                }
            }
            using (var cmd = this.DbConnection.CreateCommand())
            {
                cmd.CommandText = commandText;
                cmd.CommandType = CommandType.Text;
                if (this.dbTransaction != null)
                {
                    cmd.Transaction = this.dbTransaction;
                }
                if (dbParameters != null && dbParameters.Length > 0)
                {
                    cmd.Parameters.AddRange(dbParameters);
                }
                int milliEnd = Environment.TickCount;
                this.WriteLog(commandText, TimeSpan.FromMilliseconds(milliEnd - milliStart).TotalMilliseconds);
                result= cmd.ExecuteNonQuery();
            }
            if (this.AutoOpenClose)
            {
                this.Close();
            }
            return result;
        }

        public object ExecuteScalar(string commandText)
        {
            return this.ExecuteScalar(commandText,null);
        }
        
        public object ExecuteScalar(string commandText, DbParameter[] dbParameters)
        {
            int milliStart = Environment.TickCount;
            var concurrencyDetector = this.Database.GetService<IConcurrencyDetector>();

            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = this.Database.GetService<IRawSqlCommandBuilder>().Build(commandText, dbParameters);
                object value = rawSqlCommand.RelationalCommand.ExecuteScalar(new RelationalCommandParameterObject(this.Database.GetService<IRelationalConnection>(),
                     parameterValues: rawSqlCommand.ParameterValues, null, this,null));
                int milliEnd = Environment.TickCount;
                this.WriteLog(commandText, TimeSpan.FromMilliseconds(milliEnd - milliStart).TotalMilliseconds);
                return value;
            }
        }

        public object ExecuteScalar(CommandType commandType, string commandText, DbParameter[] dbParameters)
        {
            int milliStart = Environment.TickCount;
            object result = null;
            // 自动打开
            // 自动打开
            if (this.AutoOpenClose)
            {
                this.Open();
            }
            else
            {
                if (this.DbConnection == null)
                {
                    this.AutoOpenClose = true;
                    this.Open();
                }
            }
            using (var cmd = this.DbConnection.CreateCommand())
            {
                cmd.CommandText = commandText;
                cmd.CommandType = commandType;
                if ((dbParameters != null) && (dbParameters.Length > 0))
                {
                    cmd.Parameters.AddRange(dbParameters);
                }
                if (this.dbTransaction != null)
                {
                    cmd.Transaction = this.dbTransaction;
                }
                int milliEnd = Environment.TickCount;
                this.WriteLog(commandText, TimeSpan.FromMilliseconds(milliEnd - milliStart).TotalMilliseconds);
                result = cmd.ExecuteScalar();
            }
            // 自动打开
            if (this.AutoOpenClose)
            {
                this.Close();
            }

            return result;
        }

      

        public int ExecuteProcedure(string procedureName)
        {
            return this.ExecuteProcedure(procedureName,null);
        }

        public int ExecuteProcedure(string procedureName, DbParameter[] dbParameters)
        {
            int milliStart = Environment.TickCount;
            int result = 0;
            // 自动打开
            if (this.AutoOpenClose)
            {
                this.Open();
            }
            else
            {
                if (this.DbConnection == null)
                {
                    this.AutoOpenClose = true;
                    this.Open();
                }
            }
            using (var cmd = this.DbConnection.CreateCommand())
            {
                cmd.CommandText = procedureName;
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                if (this.dbTransaction != null)
                {
                    cmd.Transaction = this.dbTransaction;
                }
                if ((dbParameters != null) && (dbParameters.Length > 0))
                {
                    cmd.Parameters.AddRange(dbParameters);
                }
                int milliEnd = Environment.TickCount;
                result = cmd.ExecuteNonQuery();
                this.WriteLog(procedureName, TimeSpan.FromMilliseconds(milliEnd - milliStart).TotalMilliseconds);
            }
            if (this.AutoOpenClose)
            {
                this.Close();
            }
            return result;
        }

        public DataTable ExecuteProcedureForDataTable(string procedureName, string tableName, DbParameter[] dbParameters)
        {
            DataTable dataTable = new DataTable();
            int milliStart = Environment.TickCount;
            // 自动打开
            if (this.AutoOpenClose)
            {
                this.Open();
            }
            else
            {
                if (this.DbConnection == null)
                {
                    this.AutoOpenClose = true;
                    this.Open();
                }
            }
            using (var cmd = this.DbConnection.CreateCommand())
            {
                cmd.CommandText = procedureName;
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                if ((dbParameters != null) && (dbParameters.Length > 0))
                {
                    cmd.Parameters.AddRange(dbParameters);
                }
                var dbDataAdapter = GetDataAdapter();
                dbDataAdapter.SelectCommand = cmd;
                dbDataAdapter.Fill(dataTable);
                dbDataAdapter.SelectCommand.Parameters.Clear();
                int milliEnd = Environment.TickCount;
                this.WriteLog(procedureName, TimeSpan.FromMilliseconds(milliEnd - milliStart).TotalMilliseconds);
            }
            if (this.AutoOpenClose)
            {
                this.Close();
            }
            return dataTable;
        }

        public DbTransaction Begin()
        {
           
            this.AutoOpenClose = false;
            if (!this.InTransaction || this.dbTransaction == null || this.dbTransaction.Connection.State != ConnectionState.Open)
            {

                this.InTransaction = true;
                var transaction = this.Database.BeginTransaction();
                this.dbTransaction=  transaction.GetDbTransaction();
            }
            return this.dbTransaction;
          
        }

        public void Commit()
        {
            if (this.InTransaction)
            {
                // 事务已经完成了，一定要更新标志信息
                this.InTransaction = false;
                if (this.dbTransaction != null && this.dbTransaction.Connection.State == ConnectionState.Open)
                {
                    this.Database.CommitTransaction();
                    //this.dbTransaction.Commit();
                    // this.dbTransaction.Connection.Close();
                }
                 this.AutoOpenClose = true;

            }
            //this.Database.CommitTransaction();
        }

        public void Rollback()
        {
            if (this.InTransaction)
            {
                this.InTransaction = false;

                if (this.dbTransaction != null && this.dbTransaction.Connection != null && this.dbTransaction.Connection.State == ConnectionState.Open)
                {
                    this.Database.RollbackTransaction();
                   // this.dbTransaction.Rollback();
                    // this.dbTransaction.Connection.Close();
                }
                this.AutoOpenClose = true;
            }
        }

        public void Close()
        {
#if (DEBUG)
            int milliStart = Environment.TickCount;
            Trace.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " :Begin: " + MethodBase.GetCurrentMethod().ReflectedType.Name + "." + MethodBase.GetCurrentMethod().Name);
#endif

            if (this.dbConnection != null&& this.dbConnection.State!=ConnectionState.Closed)
            {
                this.dbConnection.Close();
                //this.dbConnection.Dispose();
            }
            this.dbConnection = null;
            // 写入调试信息
#if (DEBUG)
            int milliEnd = Environment.TickCount;
            Trace.WriteLine(DateTime.Now.ToString("HH:mm:ss") + " Ticks: " + TimeSpan.FromMilliseconds(milliEnd - milliStart).ToString() + " :End: " + MethodBase.GetCurrentMethod().ReflectedType.Name + "." + MethodBase.GetCurrentMethod().Name);
#endif
        }
        /// <summary>
        /// 写入sql查询日志
        /// </summary>
        /// <param name="commandText">sql查询</param>
        public void WriteLog(string commandText, double tickTimes)
        {
            LogHelper.SQLInfo(commandText, tickTimes,"EF",this.DbProviderType.ToString());
        }

        public DbConnection GetConnection(string connectionString)
        {
            return this.Database.GetDbConnection();
        }

        public DbDataAdapter GetDataAdapter()
        {
            throw new NotImplementedException();
        }

        public int SelectIdentity(string primaryKeyName)
        {
            object resultValue = this.ExecuteScalar(CommandType.Text, Identity(primaryKeyName), null);
            return resultValue.ToInt();
        }

        public abstract string Now();

        public DateTime GetNow()
        {
            object resultValue = this.ExecuteScalar(CommandType.Text, Now(), null);
            return ConvertHelper.ToDateTime(resultValue);
        }

        public abstract DbParameter[] GetParameter(List<DataParameter> parameters);

        public abstract DbParameter GetParameter(string name, object value);

        public abstract string GetParameter(string name);

        public abstract string GetLikeParameter(string parameter);

        public abstract string Identity(string where);

        public abstract void SqlBulkCopyData(DataTable dataTable);

        public abstract string Identity_Disabled(string tableName);

        public abstract string Identity_Enable(string tableName);

        public abstract DbParameter MakeOutParam(string paramName, DbType DbType, int Size);

        public abstract DbParameter MakeInParam(string paramName, DbType DbType, int Size, object Value);

        public abstract DbParameter MakeParam(string paramName, DbType DbType, int Size, ParameterDirection Direction, object Value);
        
        public List<T> GetReader<T>(string sql, DataParameterCollection dataParam) where T : new()
        {
            List<T> lstT = new List<T>();
            var dbReader = this.ExecuteReader(sql, dataParam.ToArray());
            try
            {
                while (dbReader.Read())
                {
                    var entity = DataProxy.ReaderToEntity<T>(dbReader);
                    if (entity != null)
                    {
                        lstT.Add(entity);
                    }

                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                dbReader.Close();
            }
            return lstT;
        }

        #endregion

        #region IQuery

        public abstract string GetRecordCountSql(string tableName);

        public abstract string GetSelectSql(string tableName, string queryFieldBuilder, bool isDeleted);

        /// <summary>
        /// 获取查询总数sql字符串
        /// </summary>
        /// <param name="hql"></param>
        /// <param name="createSql"></param>
        /// <returns></returns>
        public abstract string RecordCount(string hql, bool createSql = true);
        /// <summary>
        /// 获取查询结果字符串
        /// </summary>
        /// <param name="hql"></param>
        /// <param name="createSql"></param>
        /// <returns></returns>
        public abstract string QueryPage(string hql, Order[] orders, int pageIndex, int pageSize);
        #endregion

        #region IDbSource
        public abstract string GetTable(string DBName);

        public abstract string GetView(string DBName);

        public abstract string GetDbColumn(string tableName, string dbServerName, bool IsTable);

        public abstract string GetSource(string tableName);


        public abstract string CreateTable(string table, List<DbColumnAttribute> dbColumns);

        public abstract string AddColumn(string table, DbColumnAttribute dbColumns);

        public abstract string DeleteTable(string table);

        public abstract string DeleteColumn(string table, string columnName);
        /// <summary>
        /// 更改字段描述
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="columnName"></param>
        /// <param name="description"></param>
        /// <param name="isInsert"></param>
        /// <returns></returns>
        public abstract string UpdateDescriptionSql(string tableName, string columnName, string description, bool isAdd);

        /// <summary>
        /// 判断描述是否存在
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="columnName">字段名</param>
        /// <returns></returns>
        public abstract string ExistDescriptionSql(string tableName, string columnName);
        #endregion
    }
}